home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer)…68k, x86, SPARC, PA-RISC] / NeXTSTEP 3.3 Dev Intel.iso / NextDeveloper / Examples / AppKit / Backspace / BoinkViewPart.m < prev    next >
Encoding:
Text File  |  1993-07-15  |  10.1 KB  |  509 lines

  1. //  BoinkViewPart.m
  2. //
  3. //  implements a bouncing ball screen saver view
  4. //
  5. //  You may freely copy, distribute, and reuse the code in this example.
  6. //  NeXT disclaims any warranty of any kind, expressed or  implied, as to its
  7. //  fitness for any particular use.
  8.  
  9.  
  10. #import "BoinkViewPart.h"
  11. #import "SpaceView.h"
  12. #import "Thinker.h"
  13. #import "BoinkWraps.h"
  14. #import <appkit/NXImage.h>
  15. #import <math.h>
  16. #import <libc.h>
  17. #import <dpsclient/wraps.h>
  18.  
  19. @implementation BoinkView
  20.  
  21. // assumed interval in milliseconds
  22. #define ASSUMED_INTERVAL 35
  23.  
  24. #define WIDTH 100
  25. #define HEIGHT 100
  26. #define GAP 4
  27. #define COUNT 10
  28. #define ACCEL (-2)
  29. #define REBOUND (-1.3)
  30.  
  31. // This screen height value is not critical, though it will be used
  32. // to determine how high the ball can go
  33. #define SCREEN_HEIGHT 832
  34. #define LAUNCH_SPEED (sqrt(fabs(2*ACCEL*(SCREEN_HEIGHT - HEIGHT))))
  35. #define REAL_LAUNCH_SPEEd (sqrt(fabs(2*accel*(viewHeight - HEIGHT))))
  36.  
  37. #define MIN_X_SPEED (3)
  38. #define MAX_X_SPEED (6)
  39. #define ABS_MAX_X_SPEED (6)
  40. #define MAX_Y_SPEED (LAUNCH_SPEED + 20)
  41.  
  42. #define BUFFER_WIDTH (WIDTH + ABS_MAX_X_SPEED + 1)
  43. #define BUFFER_HEIGHT (HEIGHT + MAX_Y_SPEED + 1)
  44.  
  45.  
  46.  
  47. /* move the ball to its new bounce position */
  48. - oneStep
  49. {
  50.     NXRect black = {0,0,0,0};
  51.     NXRect ballRect;
  52.     BRECT new;
  53.     float scaledTime, calcYpos;
  54.     
  55.     then = now;
  56.     now = currentTimeInMs();
  57.  
  58.     /* calculate new ball x position */
  59.     xpos += [self timeCorrectedXSpeed];
  60.  
  61.     if (xpos < 0)                /* ball hit left edge */
  62.     {    xspeed = -xspeed;
  63.         if (viewWidth > WIDTH)
  64.         {    spinDir = -spinDir;
  65.         }
  66.         xpos = 0;
  67.     }
  68.     else if (xpos > (viewWidth - WIDTH))        /* ball hit right edge */
  69.     {    if (viewWidth > WIDTH)
  70.         {
  71.             xspeed = -[self getRandomXspeed];
  72.             [self checkXspeed:&xspeed];
  73.             xpos = (viewWidth - WIDTH);
  74.         }
  75.         else
  76.         {    xspeed = xpos = 0;
  77.         }
  78.     }
  79.  
  80.  
  81.     scaledTime = ((float)(now - then) / ASSUMED_INTERVAL);
  82.     if (scaledTime > 1) scaledTime = 1;
  83.     
  84.     // calculate new ball vertical position
  85.     calcYpos = ypos + (scaledTime*yspeed) + ((accel * scaledTime * scaledTime)/2);
  86.  
  87.     // change vertical ball speed to simulate gravity
  88.     yspeed += (accel * scaledTime);
  89.     
  90.     if (calcYpos < (ypos - MAX_Y_SPEED)) calcYpos = ypos - MAX_Y_SPEED;
  91.     else if (calcYpos > (ypos + MAX_Y_SPEED)) calcYpos = ypos + MAX_Y_SPEED;
  92.     
  93.     ypos = calcYpos;
  94.     
  95.     if (yspeed < -MAX_Y_SPEED) yspeed = -MAX_Y_SPEED;
  96.     
  97.  
  98.     if (ypos <= 0)                /* ball hit bottom of window */
  99.     {
  100.         ypos = 0;
  101.         
  102.         if (viewHeight > HEIGHT)
  103.         {
  104.             if (reboundMode == DECREASING)
  105.             {
  106.                 yspeed = lastLaunchSpeed = lastLaunchSpeed + rebound;
  107.             }
  108.             else
  109.             {
  110.                 yspeed = lastLaunchSpeed = lastLaunchSpeed - (2*rebound);
  111.             }
  112.  
  113.             if (yspeed <= 0)
  114.             {
  115.                 yspeed = 0;
  116.                 reboundMode = INCREASING;    /* bounce height increases every bounce */
  117.             }
  118.             else if (yspeed > MAX_Y_SPEED) yspeed = MAX_Y_SPEED - (3*accel);
  119.         }
  120.         else yspeed = 0;
  121.     
  122.     }
  123.     else if (ypos >= (viewHeight - HEIGHT))    /* ball hit top of window */
  124.     {    if (viewHeight > HEIGHT)
  125.         {
  126.             yspeed = accel;
  127.             ypos = (viewHeight - HEIGHT);
  128.             reboundMode = DECREASING;    /* bounce height decreases every bounce */
  129.             spinDir = -spinDir;
  130.         }
  131.         else
  132.         {    yspeed = ypos = 0;
  133.         }
  134.     }
  135.  
  136.  
  137.     /* rotate the ball by selecting a new ball image to blit */
  138.     /* we have an image of the ball in 10 different stages of rotation */
  139.  
  140.     [self incrementBallNumber];
  141.     
  142.     new.l = floor(xpos);
  143.     new.b = floor(ypos);
  144.     new.r = new.l + WIDTH;
  145.     new.t = new.b + HEIGHT;
  146.     
  147.     ballRect.origin.x = (WIDTH+GAP) * ballNum;
  148.     ballRect.origin.y = 0;
  149.     ballRect.size.width = WIDTH;
  150.     ballRect.size.height = HEIGHT;
  151.     
  152.     redrawTo.x = MIN(new.l, old.l);
  153.     redrawTo.y = MIN(new.b, old.b);
  154.  
  155.     redraw.origin.x = 0;
  156.     redraw.origin.y = 0;
  157.     redraw.size.width = (MAX(new.r, old.r)) - redrawTo.x + 1;
  158.     redraw.size.height = (MAX(new.t, old.t)) - redrawTo.y + 1;
  159.     
  160.     black.size= redraw.size;
  161.  
  162.     [self updateGrid];
  163.  
  164.     [buffer lockFocus];
  165.     PSsetgray(0);
  166.     NXRectFill(&black);
  167.     
  168.     ballTo.x = new.l - redrawTo.x;
  169.     ballTo.y = new.b - redrawTo.y;
  170.  
  171.     [self drawLinesInBuffer];
  172.     
  173.     [balls composite:NX_SOVER fromRect:&ballRect toPoint:&ballTo];
  174.     [buffer unlockFocus];
  175.     
  176.     
  177.     // Now bring it onto the screen
  178.     
  179.     [buffer composite:NX_COPY fromRect:&redraw toPoint:&redrawTo];
  180.  
  181.     old = new;
  182.  
  183.     return self;
  184. }
  185.  
  186.  
  187.  
  188. /* calculate a vertical launch speed which will get the ball almost to    */
  189. /* the top of the window before gravity pulls it back down. Little bit    */
  190. /* of physics lesson here...                                            */
  191.  
  192. - newSpeed
  193. {
  194.     lastLaunchSpeed = yspeed = REAL_LAUNCH_SPEEd;
  195.     if (yspeed > MAX_Y_SPEED) yspeed = MAX_Y_SPEED;
  196.     xpos = 0;
  197.     ypos = 0;
  198.  
  199.     if (viewWidth <= WIDTH) xspeed = 0;
  200.     else xspeed = [self getRandomXspeed];
  201.  
  202.     [self checkXspeed:&xspeed];
  203.     rebound = REBOUND;
  204.     return self;
  205. }
  206.  
  207.  
  208. - initFrame:(const NXRect *)frameRect
  209. {
  210.     NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };
  211.  
  212.     [super initFrame:frameRect];
  213.     [self allocateGState];        // For faster lock/unlockFocus
  214.     [self setClipping:NO];        // even faster...
  215.  
  216.     accel = ACCEL;
  217.     spinDir = 1;
  218.  
  219.     //in this case, I only need one buffer for several Views
  220.     if (!(buffer = [NXImage findImageNamed:"boinkBuffer"]))
  221.     {
  222.         buffer = [[NXImage alloc] initSize:&black.size];
  223.         [buffer setName:"boinkBuffer"];
  224.     }
  225.     
  226.     if ([buffer lockFocus])
  227.     {
  228.         PSsetgray(0);
  229.         NXRectFill(&black);
  230.         [buffer unlockFocus];
  231.     }
  232.  
  233.     balls = [NXImage findImageNamed:"balls"];
  234.  
  235.     [self newViewSize];
  236.  
  237.     return self;
  238. }
  239.  
  240. - setAccel:(float)val
  241. {
  242.     accel = val;
  243.     return self;
  244. }
  245.  
  246. - sizeTo:(NXCoord)width :(NXCoord)height
  247. {
  248.     [super sizeTo:width :height];
  249.     [self newViewSize];
  250.     return self;
  251. }
  252.  
  253. - drawSelf:(const NXRect *)rects :(int)rectCount
  254. {
  255.     if (!rects || !rectCount) return self;
  256.     
  257.     //PSsetgray(0);
  258.     //NXRectFill(rects);
  259.     
  260.     NXRectClip(rects);
  261.     [self drawGrid];
  262.  
  263.     return self;
  264. }
  265.  
  266. - newViewSize
  267. {
  268.     int i;
  269.     //this is called every time View size changes
  270.     NXRect black = {0, 0, BUFFER_WIDTH, BUFFER_HEIGHT };
  271.  
  272.     then = now = currentTimeInMs();
  273.  
  274.     if (oldSize.width == bounds.size.width &&
  275.             oldSize.height == bounds.size.height)
  276.         return self;
  277.     else
  278.     {
  279.         oldSize.width = bounds.size.width;
  280.         oldSize.height = bounds.size.height;
  281.     }
  282.     
  283.     old.l = old.r = old.b = old.t = ballTo.x = ballTo.y = 0;
  284.  
  285.     viewWidth = bounds.size.width;
  286.     viewHeight = bounds.size.height;
  287.     if (viewHeight > SCREEN_HEIGHT) viewHeight = SCREEN_HEIGHT;
  288.     
  289.     nvert = viewWidth/130;
  290.     if (nvert > NVERT) nvert = NVERT;
  291.     nhoriz = viewHeight/130;
  292.     if (nhoriz > NHORIZ) nhoriz = NHORIZ;
  293.     
  294.     if (viewWidth < WIDTH) nvert = 0;
  295.     if (viewHeight < HEIGHT) nhoriz= 0;
  296.     vcount = hcount = 0;
  297.     
  298.     for (i=0; i<nvert; i++)
  299.     {
  300.         vertLines[i].hue = i * 0.17;
  301.         while (vertLines[i].hue > 1) vertLines[i].hue -= 1;
  302.         vertLines[i].pos = floor(i * (viewWidth/nvert));
  303.     }
  304.     
  305.     for (i=0; i<nhoriz; i++)
  306.     {
  307.         horizLines[i].hue = i * 0.17 + 0.1;
  308.         while (horizLines[i].hue > 1) horizLines[i].hue -= 1;
  309.         horizLines[i].pos = i * floor((viewHeight/nhoriz)) + 1;
  310.     }
  311.     
  312.     if ([buffer lockFocus])
  313.     {
  314.         PSsetgray(0);
  315.         NXRectFill(&black);
  316.         [buffer unlockFocus];
  317.     }
  318.  
  319.     [self newSpeed];
  320.     return self;
  321. }
  322.  
  323. - incrementBallNumber
  324. {
  325.     if (now > nextRotationTime)
  326.     {
  327.         ballNum += spinDir;
  328.  
  329.         if (ballNum >= COUNT) ballNum = 0;
  330.         else if (ballNum < 0) ballNum = COUNT-1;
  331.         nextRotationTime = now + 24;
  332.     }
  333.  
  334.     return self;
  335. }
  336.  
  337. - (float) getRandomXspeed
  338. {
  339.     return randBetween(MIN_X_SPEED, MAX_X_SPEED);
  340. }
  341.  
  342. - (float) timeCorrectedXSpeed
  343. {
  344.     float ret = xspeed * ((float)(now - then) / ASSUMED_INTERVAL);
  345.     [self checkXspeed:&ret];
  346.     return ret;
  347. }
  348.  
  349. - checkXspeed:(float *)speed
  350. {
  351.     if (*speed > MAX_X_SPEED) *speed = MAX_X_SPEED;
  352.     else if (*speed < -MAX_X_SPEED) *speed = -MAX_X_SPEED;
  353.     return self;
  354. }
  355.  
  356. - (const char *)windowTitle
  357. {
  358.     return "Boink!";
  359. }
  360.  
  361.  
  362. - drawGrid
  363. {
  364.     int i;
  365.     float *fp;
  366.         
  367.     for (i=0; i<nvert; i++)
  368.     {
  369.         fp = &vertLines[i].pos;
  370.         colorLine(*fp, 0, *fp, viewHeight, vertLines[i].hue, 1);
  371.     }
  372.     
  373.     for (i=0; i<nhoriz; i++)
  374.     {
  375.         fp = &horizLines[i].pos;
  376.         colorLine(0, *fp, viewWidth, *fp, horizLines[i].hue, 1);
  377.     }
  378.     
  379.     return self;
  380. }
  381.  
  382. - updateGrid
  383. {
  384.     NXRect avoid;
  385.     float oldPos;
  386.     float *fp;
  387.     
  388.     if (!nvert && !nhoriz) return self;
  389.  
  390.     if (now < nextLineDrawTime) return self;
  391.  
  392.     nextLineDrawTime = now + 3300;
  393.     
  394.     avoid.origin = redrawTo;
  395.     avoid.size = redraw.size;
  396.  
  397.     if (++toggle & 1)
  398.     {
  399.         //advance vertical line
  400.         
  401.         if (!nvert) return self;
  402.         
  403.         fp = &vertLines[vcount].pos;
  404.         oldPos = *fp;
  405.         *fp += 1;
  406.         if (*fp > viewWidth) *fp = 0;
  407.         vertLines[vcount].hue += 0.005;
  408.         if (vertLines[vcount].hue > 1) vertLines[vcount].hue -= 1;
  409.         
  410.  
  411.         verticalLineWithAvoidance(*fp, 0, *fp, viewHeight, vertLines[vcount].hue, 1, &avoid);
  412.         verticalLineWithAvoidance(oldPos, 0, oldPos, viewHeight, 0, 0, &avoid);
  413.  
  414.         if (++vcount >= nvert) vcount = 0;
  415.     }
  416.     else
  417.     {
  418.         //advance horiz line
  419.         
  420.         if (!nhoriz) return self;
  421.         
  422.         fp = &horizLines[hcount].pos;
  423.         oldPos = *fp;
  424.         *fp += 1;
  425.         if (*fp > viewHeight) *fp = 0;
  426.         horizLines[hcount].hue += 0.005;
  427.         if (horizLines[hcount].hue > 1) horizLines[hcount].hue -= 1;
  428.         
  429.  
  430.         horizLineWithAvoidance(0, *fp, viewWidth, *fp, horizLines[hcount].hue, 1, &avoid);
  431.         horizLineWithAvoidance(0, oldPos, viewWidth, oldPos, 0, 0, &avoid);
  432.  
  433.         if (++hcount >= nhoriz) hcount = 0;
  434.     }
  435.     
  436.     return self;
  437. }
  438.  
  439. void horizLineWithAvoidance(float x1, float y1, float x2,float y2,
  440.             float hue,float brightness, const NXRect *r)
  441. {
  442.     if (y1 <= r->origin.y || y1 >= r->origin.y+r->size.height)
  443.         colorLine(x1, y1, x2, y2, hue, brightness);
  444.     else
  445.     {
  446.         colorLine(x1, y1, r->origin.x, y2, hue, brightness);
  447.         colorLine(r->origin.x+r->size.width, y1, x2, y2, hue, brightness);
  448.     }
  449. }
  450.  
  451. void verticalLineWithAvoidance(float x1, float y1, float x2,float y2,
  452.             float hue,float brightness, const NXRect *r)
  453. {
  454.     if (x1 <= r->origin.x || x1 >= r->origin.x+r->size.width)
  455.         colorLine(x1, y1, x2, y2, hue, brightness);
  456.     else
  457.     {
  458.         colorLine(x1, y1, x2, r->origin.y, hue, brightness);
  459.         colorLine(x1, r->origin.y+r->size.height, x2, y2, hue, brightness);
  460.     }
  461. }
  462.  
  463. - drawLinesInBuffer
  464. {
  465.     NXRect avoid;
  466.     int i;
  467.         
  468.     avoid.origin = redrawTo;
  469.     avoid.size = redraw.size;
  470.  
  471.     for (i=0; i<nvert; i++)
  472.     {
  473.         float x = vertLines[i].pos;
  474.  
  475.         if (x >= avoid.origin.x && x <= avoid.origin.x + avoid.size.width)
  476.         {
  477.             colorLine(x-redrawTo.x, 0, x-redrawTo.x, avoid.size.height, vertLines[i].hue, 1);
  478.         }
  479.     }
  480.     
  481.     for (i=0; i<nhoriz; i++)
  482.     {
  483.         float y = horizLines[i].pos;
  484.  
  485.         if (y >= avoid.origin.y && y <= avoid.origin.y + avoid.size.height)
  486.         {
  487.             colorLine(0, y-redrawTo.y, avoid.size.width, y-redrawTo.y, horizLines[i].hue, 1);
  488.         }
  489.     }
  490.     
  491.     return self;
  492. }
  493.  
  494. - inspector:sender
  495. {
  496.     return [sender boinkInspector];
  497. }
  498.  
  499.  
  500.  
  501. @end
  502.  
  503.  
  504.  
  505.  
  506.  
  507.  
  508.  
  509.